using System;
using EnvDTE;
using System.Xml;
using System.Xml.XPath ;
using Extensibility;
using System.Collections ;

using System.Windows .Forms ;
using System.CodeDom .Compiler ;
using System.CodeDom;
using System.Text ;
using System.Text .RegularExpressions ;
using AopDotNet.Editor;

namespace AopDotNet
{
	namespace CodeModelXMLTreeBuilder
	{
		/// <summary>
		/// builds an xml tree that represents the code model.
		/// </summary>
		public class CodeModelBuilder
		{
			public Hashtable codeElements;
			private XmlDocument XmlCodeModelDoc;
			private  XmlElement Root;
			private ICodeGenerator codeGenerator;
			private EditingMonitor editingMonitor;
			public CodeModelBuilder(ICodeGenerator codegen,EditingMonitor editMon)
			{
				editingMonitor=editMon;
			
				XmlCodeModelDoc =new XmlDocument();
			
				codeElements=new Hashtable();

				XmlDeclaration dec=XmlCodeModelDoc.CreateXmlDeclaration ("1.0","","");
				XmlCodeModelDoc .AppendChild (dec);
				
				Root=XmlCodeModelDoc.CreateElement ("codemodel");
				XmlCodeModelDoc.InsertAfter (Root,dec);
				codeGenerator =codegen;
			}
			public void BuildTreeFromCodeElements(CodeElements elements)
			{
				bool CodeAdded=false;
				recurseClasses(elements,Root,ref CodeAdded);
			}
			public XmlDocument GetXmlDocument()
			{
				return XmlCodeModelDoc ;
			}
			public void recurseClasses(CodeElements codeElements,XmlElement Parent,ref bool CodeAdded)
			{
				if (codeElements.Count == 0)
				{
					CodeAdded =false;
					return;
				}
				foreach (CodeElement el in codeElements)
				{
					switch (el.Kind)
					{
						case vsCMElement.vsCMElementNamespace:
							AddNameSpace ((EnvDTE.CodeNamespace)el);
							break;

						case vsCMElement.vsCMElementClass :
							bool CodeAdded2=false;
							AddClass((CodeClass)el,Parent,ref CodeAdded2);
							CodeAdded=CodeAdded2;
							break;

						case vsCMElement.vsCMElementFunction :
							AddFunction((CodeFunction) el,Parent,false);
							break;
						case vsCMElement.vsCMElementProperty :
							AddProperty((CodeProperty)el,Parent);
							break;
						case vsCMElement.vsCMElementVariable:
							AddVariable((CodeVariable)el,Parent);
							break;
						case vsCMElement.vsCMElementAttribute :
							AddAttribute((CodeAttribute)el,Parent);
							break;
						case vsCMElement.vsCMElementParameter :
							AddParameter((CodeParameter)el,Parent);
							break;
						case vsCMElement.vsCMElementInterface :
							AddInterface((CodeInterface )el,Parent);
							break;
						case vsCMElement.vsCMElementImportStmt :
						case vsCMElement.vsCMElementIncludeStmt:
						case vsCMElement.vsCMElementUsingStmt :
						
							AddUsingStatement(el,Parent);
							break;
					}
				}
			}

			public void AddNameSpace(EnvDTE.CodeNamespace nSpace)
			{
				XmlNode node=Root.SelectSingleNode ("//namespaces");
				if (node==null)
				{
					XmlElement namespacesElement=XmlCodeModelDoc.CreateElement ("namespaces");
					Root.AppendChild (namespacesElement);
					Root=namespacesElement;
				}
				else
					Root=(XmlElement)node;

				XmlElement namespaceElement=XmlCodeModelDoc.CreateElement ("namespace") ;
				Root.AppendChild (namespaceElement);
				
				XmlAttribute nameChild=XmlCodeModelDoc.CreateAttribute ("name");
				nameChild.Value =nSpace.Name ;
				namespaceElement.SetAttributeNode (nameChild);
			
				XmlAttribute idChild=XmlCodeModelDoc.CreateAttribute ("id");
				idChild.Value =nSpace.GetHashCode ().ToString () ;
				namespaceElement.SetAttributeNode(idChild);

				codeElements.Add (nSpace.GetHashCode (),nSpace);
				
				bool CodeAdded=false;
				recurseClasses(nSpace.Members,namespaceElement,ref CodeAdded);
				if (CodeAdded==false)
					Root.RemoveChild (namespaceElement);
				//}
			}
			public void AddClass(CodeClass cClass,XmlElement Parent,ref bool CodeAdded)
			{
				if (cClass.InfoLocation == vsCMInfoLocation.vsCMInfoLocationProject)
				{
					string elementName="class",parentName="classes";
					//string [] bases=new string [2];
					//bases[0]="AspectLib.Aspect";
					//bases[1]="Aspect";
					try
					{
						if (cClass.get_IsDerivedFrom ("AspectLib.Aspect"))
						{
							elementName="aspect";
							parentName="aspects";
						}
						else
						{
							elementName="class";
							parentName="classes";
						
						}
					}
					catch (Exception exp)
					{
					}
				
					XmlNode node=Parent .SelectSingleNode (parentName);
					if (node==null)
					{
						XmlElement theElement=XmlCodeModelDoc.CreateElement (parentName);
						Parent.AppendChild (theElement);
						Parent=theElement;
					}
					else
						Parent=(XmlElement)node;
				
					
					/*if (IsDerivedFrom (cClass,bases,codeGenerator )==true)
					{
						elementName="aspect";
						XmlNode node=Parent .SelectSingleNode ("aspects");
						if (node==null)
						{
							XmlElement aspectsElement=XmlCodeModelDoc.CreateElement ("aspects");
							Parent.AppendChild (aspectsElement);
							Parent=aspectsElement;
						}
						else
							Parent=(XmlElement)node;
			
					}*/
					XmlElement classElement=XmlCodeModelDoc.CreateElement (elementName) ;
					Parent.AppendChild (classElement);
				
					XmlAttribute  nameChild=XmlCodeModelDoc.CreateAttribute ("name");
					nameChild.Value =cClass.Name ;
					classElement.SetAttributeNode (nameChild);
				
					XmlAttribute  idChild=XmlCodeModelDoc.CreateAttribute ("id");
					idChild.Value =cClass.GetHashCode ().ToString () ;
					classElement.SetAttributeNode (idChild);

					codeElements.Add (cClass.GetHashCode (),cClass);
				
					CodeElements els=cClass.Members;
				
					CodeAdded=true;

					bool CodeAdded2=false;
					recurseClasses(cClass.Members,classElement,ref CodeAdded2);

					bool CodeAdded3=false;
					recurseClasses(cClass.Attributes,classElement,ref CodeAdded3);
				}
				else
					CodeAdded=false;
			}
			void AddInterface(CodeInterface cInterface,XmlElement Parent)
			{
				if (cInterface.InfoLocation == vsCMInfoLocation.vsCMInfoLocationProject)
				{
			
					XmlNode node=Parent.SelectSingleNode ("interfaces");
					if (node==null)
					{
						XmlElement interfacesElement=XmlCodeModelDoc.CreateElement ("interfaces");
						Parent.AppendChild (interfacesElement);
						Parent=interfacesElement;
					}
					else
						Parent=(XmlElement)node;
            			
					XmlElement interfaceElement=XmlCodeModelDoc.CreateElement ("interface") ;
					Parent.AppendChild (interfaceElement);
				
					XmlAttribute  nameChild=XmlCodeModelDoc.CreateAttribute ("name");
					nameChild.Value =cInterface.Name ;
					interfaceElement.SetAttributeNode (nameChild);
		
					XmlAttribute idChild=XmlCodeModelDoc.CreateAttribute ("id");
					idChild.Value =cInterface.GetHashCode ().ToString () ;
					interfaceElement.SetAttributeNode(idChild);
					codeElements.Add (cInterface.GetHashCode (),cInterface);
				
					XmlAttribute accessChild=XmlCodeModelDoc.CreateAttribute ("access");
					accessChild.Value =cInterface.Access  .ToString ();
					interfaceElement.SetAttributeNode(accessChild);
			
					bool CodeAdded2=false;
					recurseClasses(cInterface.Members,interfaceElement,ref CodeAdded2);
			
				}
			}
		
			public string GetFunctionBody(CodeFunction cf)
			{
				/*REMF*/
				EditPoint strtPoint=cf.GetStartPoint(vsCMPart.vsCMPartBody).CreateEditPoint ();
				this.editingMonitor .UpdateEditPoint (ref strtPoint);
				EditPoint endPoint=cf.GetEndPoint(vsCMPart.vsCMPartBody).CreateEditPoint ();
				editingMonitor.UpdateEditPoint (ref endPoint);
				return strtPoint.GetText(endPoint);
			}
			public void AddFunction(CodeFunction cFunc,XmlElement Parent,bool Overloaded)
			{
				XmlNode node=Parent.SelectSingleNode ("methods");
				if (node==null)
				{
					XmlElement methodsElement=XmlCodeModelDoc.CreateElement ("methods");
					Parent.AppendChild (methodsElement);
					Parent=methodsElement;
				}
				else
					Parent=(XmlElement)node;

				XmlElement functionElement=XmlCodeModelDoc.CreateElement ("method") ;
				Parent.AppendChild (functionElement);
				
				XmlAttribute  nameChild=XmlCodeModelDoc.CreateAttribute ("name");
				nameChild.Value =cFunc.Name ;
				functionElement.SetAttributeNode (nameChild);
		
				XmlAttribute idChild=XmlCodeModelDoc.CreateAttribute ("id");
				idChild.Value =cFunc.GetHashCode ().ToString () ;
				functionElement.SetAttributeNode(idChild);
				codeElements.Add (cFunc.GetHashCode (),cFunc);
				
				XmlAttribute accessChild=XmlCodeModelDoc.CreateAttribute ("access");
				accessChild.Value =cFunc.Access .ToString ();
				functionElement.SetAttributeNode(accessChild);
		
				XmlAttribute staticChild=XmlCodeModelDoc.CreateAttribute ("static");
				if (cFunc.IsShared )
					staticChild .Value ="true";
				else
					staticChild .Value ="false"; 
				functionElement.SetAttributeNode(staticChild);

				XmlAttribute returnChild=XmlCodeModelDoc.CreateAttribute ("return");
				returnChild.Value =cFunc.Type.TypeKind .ToString ();
				functionElement.SetAttributeNode(returnChild);
			
				bool CodeAdded=false;
				recurseClasses(cFunc.Parameters ,functionElement,ref CodeAdded);
			
				bool CodeAdded3=false;
				recurseClasses(cFunc.Attributes,functionElement,ref CodeAdded3);
			

			}
			public void AddParameter(CodeParameter cParam,XmlElement Parent)
			{
				XmlNode node=Parent.SelectSingleNode ("parameters");
				if (node==null)
				{
					XmlElement paramsElement=XmlCodeModelDoc.CreateElement ("parameters");
					Parent.AppendChild (paramsElement);
					Parent=paramsElement;
				}
				else
					Parent=(XmlElement)node;

				XmlElement paramElement=XmlCodeModelDoc.CreateElement ("parameter");
				Parent.AppendChild (paramElement);
				
				XmlAttribute paramNameChild=XmlCodeModelDoc.CreateAttribute ("name");
				paramNameChild.Value = cParam.Name;
				paramElement.SetAttributeNode (paramNameChild);
				
				XmlAttribute idChild=XmlCodeModelDoc.CreateAttribute ("id");
				idChild .Value =cParam.GetHashCode ().ToString () ;
				paramElement.SetAttributeNode(idChild);
				
				codeElements.Add (cParam.GetHashCode (),cParam);

				XmlAttribute typeChild=XmlCodeModelDoc.CreateAttribute ("type");
				typeChild .Value =cParam.Type .AsString;
				paramElement.SetAttributeNode(typeChild);
				
			}
	
			public void AddProperty(CodeProperty cProp,XmlElement Parent)
			{
				XmlNode node=Parent.SelectSingleNode ("properties");
				if (node==null)
				{
					XmlElement propertiesElement=XmlCodeModelDoc.CreateElement ("properties");
					Parent.AppendChild (propertiesElement);
					Parent=propertiesElement;
				}
				else
					Parent=(XmlElement)node;

				XmlElement propElement=XmlCodeModelDoc.CreateElement ("property") ;
				Parent.AppendChild (propElement);
				
				XmlAttribute  nameChild=XmlCodeModelDoc.CreateAttribute ("name");
				nameChild.Value = cProp.Name ;
				propElement.SetAttributeNode (nameChild);
						
				XmlAttribute idChild=XmlCodeModelDoc.CreateAttribute ("id");
				idChild.Value =cProp.GetHashCode ().ToString () ;
				propElement.SetAttributeNode(idChild);
						
				codeElements.Add (cProp.GetHashCode (),cProp);

				XmlAttribute accessChild=XmlCodeModelDoc.CreateAttribute ("access");
				accessChild.Value =cProp.Access .ToString ();
				propElement.SetAttributeNode(accessChild);
		
				CodeFunction getter=cProp.Getter;
				if (getter!=null)
				{
					XmlElement getterChild=XmlCodeModelDoc.CreateElement ("getter");
					propElement.AppendChild (getterChild);
					//store pos
				}
				CodeFunction setter=cProp.Setter ;
				if (setter!=null)
				{
					XmlElement setterChild=XmlCodeModelDoc.CreateElement ("setter");
					propElement.AppendChild (setterChild);
					//store pos
				}

			}
			public void AddVariable(CodeVariable cVar,XmlElement Parent)
			{
				XmlNode node=Parent.SelectSingleNode ("membervariables");
				if (node==null)
				{
					XmlElement membersElement=XmlCodeModelDoc.CreateElement ("membervariables");
					Parent.AppendChild (membersElement);
					Parent=membersElement;
				}
				else
					Parent=(XmlElement)node;

				XmlElement varElement=XmlCodeModelDoc.CreateElement ("member") ;
				Parent.AppendChild (varElement);
				
				XmlAttribute  nameChild=XmlCodeModelDoc.CreateAttribute ("name");
				nameChild.Value = cVar.Name ;
				varElement.SetAttributeNode (nameChild);
			
				XmlAttribute idChild=XmlCodeModelDoc.CreateAttribute ("id");
				idChild.Value =cVar.GetHashCode ().ToString () ;
				varElement.SetAttributeNode (idChild);

				codeElements.Add (cVar.GetHashCode (),cVar);

				XmlAttribute typeChild=XmlCodeModelDoc .CreateAttribute ("type");
				typeChild.Value = cVar.get_Prototype ((int)(vsCMPrototype.vsCMPrototypeType|vsCMPrototype.vsCMPrototypeNoName));
				varElement.SetAttributeNode (typeChild);
			
				XmlAttribute  accessChild=XmlCodeModelDoc.CreateAttribute("access");
				accessChild.Value =cVar.Access .ToString ();
				varElement.SetAttributeNode (accessChild);
			}		

			public void AddAttribute(CodeAttribute cAttribute,XmlElement Parent)
			{
				XmlNode node=Parent.SelectSingleNode ("attributes");
				if (node==null)
				{
					XmlElement attsElement=XmlCodeModelDoc.CreateElement ("attributes");
					Parent.AppendChild (attsElement);
					Parent=attsElement;
				}
				else
					Parent=(XmlElement)node;

				XmlElement attElement=XmlCodeModelDoc.CreateElement ("attribute") ;
				Parent.AppendChild (attElement);
				
				XmlAttribute  nameChild=XmlCodeModelDoc.CreateAttribute ("name");
				nameChild.Value = cAttribute.Name ;
				attElement.SetAttributeNode (nameChild);
			
				XmlAttribute idChild=XmlCodeModelDoc.CreateAttribute ("id");
				idChild.Value =cAttribute.GetHashCode ().ToString () ;
				attElement.SetAttributeNode (idChild);

				codeElements.Add (cAttribute.GetHashCode (),cAttribute);

			}
			void AddUsingStatement(CodeElement statement,XmlElement Parent)
			{
				string fileName=statement.ProjectItem.get_FileNames (0);
				//XmlNode fileNode=XmlCodeModelDoc.SelectSingleNode ("file/@name="+filename);
				XmlNode fileNode=Parent.SelectSingleNode ("file/@name="+fileName);
				if (fileNode==null)
				{
					XmlElement fileElement=XmlCodeModelDoc.CreateElement ("file");
					Parent.AppendChild (fileElement);
					Parent=fileElement;
				}
				else
					Parent=(XmlElement)fileNode;

				XmlElement importElement=XmlCodeModelDoc.CreateElement ("imported") ;
				Parent.AppendChild (importElement);
				
				XmlAttribute  nameChild=XmlCodeModelDoc.CreateAttribute ("name");
				nameChild.Value = statement.FullName ;
				importElement.SetAttributeNode (nameChild);
			
			}
			public CodeElement SearchForElement(string QueryString)
			{
			
				XmlNodeList nodes= XmlCodeModelDoc.SelectNodes (QueryString);
				foreach (XmlNode node in nodes)
				{
					if (node.SelectSingleNode ("ancestor::introductions")==null)
					{
						int id=Int32.Parse (node.Attributes ["id"].Value);
						if (codeElements.Contains (id)==false)
							return null;
						else
							return (CodeElement)codeElements[id];
					}
				}
				return null;
			}
			public XmlNodeList SearchForElements(string QueryString)
			{
				return  XmlCodeModelDoc.SelectNodes (QueryString);
			
			}
		}
	}
}